
using System.Reflection.Emit;

namespace Crux.Core.MongoDb;
/// <summary>
/// MongoUnitOfWorkOfT
/// </summary>
public class MongoUnitOfWork<TContext>(
    IMongoDbConnectionHelm<TContext> helm,
    IMongoDbResolver resolver,
    IOptions<MongoDbOption<TContext>> option) :
    IMongoUnitOfWork<TContext> where TContext : IMongoDBContext
{
    /// <inheritdoc/>
    public delegate TContext DynamicContextActivator(IMongoDatabase database);
    /// <inheritdoc/>
    private static DynamicContextActivator? activator;
    #region private fields
    private readonly Lazy<IMongoClient> _client = new Lazy<IMongoClient>(helm.GetMongoClient);
    private readonly Lazy<TContext> _context = new Lazy<TContext>(() =>
    {
        if (activator is null)
        {
            var sessionCtorType = new[] { typeof(IMongoDatabase) };

            var dynamicMethod = new DynamicMethod("CreateInstance", typeof(TContext), sessionCtorType, true);

            var ilGenerator = dynamicMethod.GetILGenerator();
            ilGenerator.Emit(OpCodes.Nop);

            var ctor = typeof(TContext).GetConstructor(sessionCtorType);
            if (ctor is null)
                throw new MissingMemberException("UnableToFindSessionConstructor");

            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Newobj, ctor);
            ilGenerator.Emit(OpCodes.Ret);

            activator = (DynamicContextActivator)dynamicMethod.CreateDelegate(typeof(DynamicContextActivator));
        }

        var databaseName = resolver.Resolve(option.Value);

        var client = helm.GetMongoClient();

        var database = client.GetDatabase(databaseName);

        return activator(database);
    });

    #endregion  private fields
    #region implements IMongoUnitOfWork
    /// <inheritdoc/>
    public TContext Context => _context.Value;
    /// <inheritdoc/>
    public IMongoClient Client => _client.Value;
    #endregion implements IMongoUnitOfWork


}